#include <gtest/gtest.h>

#include <compilation/SyntaxTree.h>
#include <compilation/instruction/InstructionVisitor.h>


extern FILE *yyin;
extern int yyparse();
extern int yydebug;

extern compilation::ProgramParseContext parseContext;


namespace compilation
{


template <InstructionType _Type>
struct get_type_value
{
  int operator() (VirtualInstruction* instruction)
  { return instruction->type(); }
  int operator() (VirtualInstruction* instruction, int& sub)
  {
    sub -= instruction->type();
    return instruction->type();
  }
};

template <>
struct get_type_value<InstructionType::TExpression>
{
  int operator() (VirtualInstruction* instruction)
  { return 42; }
  int operator() (VirtualInstruction* instruction, int& sub)
  { sub -= 42; return 42; }
};

} // compilation


namespace compilation
{


TEST(InstructionVisitor, catch_good_value)
{
  InstructionVisitor<int> visitor =
      InstructionVisitor<int>::build_visitor<get_type_value>();
  int val = -1;
  {
    LabelInstruction label;
    val = visitor(&label);
    EXPECT_EQ(val, int(InstructionType::TLabel));
  }
  {
    ExpressionInstruction expr;
    val = visitor(&expr);
    EXPECT_EQ(val, 42);
  }
}


TEST(InstructionVisitor, work_with_other_arguments)
{
  InstructionVisitor<int,int&> visitor =
      InstructionVisitor<int,int&>::build_visitor<get_type_value>();
  int val = -1;
  int base = 100;
  {
    LabelInstruction label;
    val = visitor(&label, base);
    EXPECT_EQ(val, int(InstructionType::TLabel));
    EXPECT_EQ(base, 100 - int(InstructionType::TLabel));
  }
  {
    ExpressionInstruction expr;
    val = visitor(&expr, base);
    EXPECT_EQ(val, 42);
    EXPECT_EQ(base, 100 - int(InstructionType::TLabel) - 42);
  }
}


} // compilation
